#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include "MutexBuffer.h"

MutexBuffer::MutexBuffer(const size_t& size)
:buf( 0 )
,bufSize( size )
,head( 0 )
,tail( 0 )
,nothingToRead( true )
,nowhereToWrite( false )
{
	assert ( size != 0 );
	buf = malloc( size );
	mutex.Init();
}

size_t MutexBuffer::Write( const void* data, const size_t& size )
{
	mutex.Lock();
	size_t written = WriteRecursive( data, size );

	std::cout << "Written: " << written << std::endl;

	if ( written > 0 )
		nothingToRead = false;

	mutex.Unlock();
	return written;
}

size_t MutexBuffer::Read( void* buf, const size_t& size )
{
	mutex.Lock();
	size_t read = ReadRecursive( buf, size );

	std::cout << "Read: " << read << std::endl;

	if (read > 0 )
		nowhereToWrite = false;

	mutex.Unlock();
	return read;
}

size_t MutexBuffer::GetSize()
{
	return bufSize;
}

MutexBuffer::~MutexBuffer(void)
{
	free( buf );
}

size_t MutexBuffer::WriteRecursive( const void* data, const size_t& size )
{
	if ( nowhereToWrite )
		return 0;

	if ( tail > head )
	{
		size_t freeSpace = tail - head - 1;
		size_t sizeToWrite = size < freeSpace ? size : freeSpace;
		memcpy( (char*)buf + head, data, sizeToWrite );
		head = ( head + sizeToWrite ) % bufSize;
		return sizeToWrite;
	}
	else
	{
		size_t sizeTillEnd = bufSize - head;
		if (size < sizeTillEnd)
		{
			memcpy( (char*)buf + head, data, size );
			head = ( head + size ) % bufSize;
			return size;
		}
		else
		{
			memcpy( (char*)buf + head, data, sizeTillEnd );
			head = 0;

			if ( 0 == tail )
				nowhereToWrite = true;

			return sizeTillEnd + WriteRecursive( (char*)data + sizeTillEnd, size - sizeTillEnd );
		}
	}
}

size_t MutexBuffer::ReadRecursive(void* buf, const size_t& size)
{
	if ( nothingToRead )
		return 0;

	if ( head > tail )
	{
		size_t dataSize = head - tail - 1;
		size_t sizeToRead = size < dataSize ? size : dataSize;
		memcpy( buf, (char*)this->buf + tail, sizeToRead );
		tail = ( tail + sizeToRead ) % bufSize;
		return sizeToRead;
	}
	else
	{
		size_t sizeTillEnd = bufSize - tail;
		if (size < sizeTillEnd)
		{
			memcpy ( buf, (char*)this->buf + tail, size );
			tail = ( tail + size ) % bufSize;
			return size;
		}
		else
		{
			memcpy( buf, (char*)this->buf + tail, sizeTillEnd );
			tail = 0;

			if ( 0 == head )
				nothingToRead = true;

			return sizeTillEnd + ReadRecursive( (char*)buf + sizeTillEnd, size - sizeTillEnd );
		}
	}
}
